package com.example.demo.aop;

import com.example.demo.annotate.AccessLimit;
import com.example.demo.common.Func;
import com.example.demo.common.Valid;
import com.example.demo.common.constant.RedisKey;
import com.example.demo.common.constant.ResultCode;
import com.example.demo.exception.VisitException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Order(9)
@RequiredArgsConstructor
@Slf4j
public class LimitAop {

    private final RedisTemplate<String, String> redis;

    @Pointcut("@annotation(com.example.demo.annotate.AccessLimit)")
    public void LimitAop() {

    }

    @Around("LimitAop()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // redis的实体类没有被注入初始化，出错
        if (redis == null) {
            log.info(ResultCode.USER_REDIS_INIT_ERROR.getMessage());
            throw new VisitException(ResultCode.USER_REDIS_INIT_ERROR);
        }

        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);

        // 从redis中判断是否在被禁的IP列表内（可移放至防火墙过滤器中）
        HttpServletRequest request = Func.getHttpRequest();
        String ip = Func.getIP(request);
        List<String> ipList = redis.opsForList().range(RedisKey.CONFIG_BLACKLIST_LIST, 0, -1);
        if (ipList.contains(ip)) {
            log.info(ip + ResultCode.USER_IP_PROHIBIT.getMessage());
            throw new VisitException(ResultCode.USER_IP_PROHIBIT);
        }

        // 从redis中判断用户访问的次数
        int maxCount = accessLimit.maxCount();
        String key = String.format(RedisKey.LIMIT + "%s:%s", ip, request.getServletPath());
        String count = redis.opsForValue().get(key);
        if (Valid.isEmpty(count)) {
            redis.opsForValue().set(key, "1", accessLimit.seconds(), TimeUnit.SECONDS);
        }
        else {
            if (Integer.parseInt(count) < maxCount) {
                redis.opsForValue().set(key, String.valueOf(Integer.parseInt(count) + 1), accessLimit.seconds(), TimeUnit.SECONDS);
            }
            else {
                redis.opsForList().rightPush(RedisKey.CONFIG_BLACKLIST_LIST, ip);
                redis.expire(RedisKey.CONFIG_BLACKLIST_LIST, 60, TimeUnit.SECONDS);

                log.info(ip + ResultCode.USER_IP_LIMITOUT.getMessage());
                throw new VisitException(ResultCode.USER_IP_LIMITOUT);
            }
        }

        return joinPoint.proceed();
    }
}
